<?php
/**
 * Filter to turn xml reference data into xhtml
 * Links to local/references library so user can re-use reference data
 *
 * @copyright &copy; 2009 The Open University
 * @author j.platts@open.ac.uk
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package filter/references
 */
class filter_references extends moodle_text_filter {

    private static $counter = 0;//number of times used
    private static $randnum = 0;

    public function filter ($text, array $options = array()) {
        global $CFG, $COURSE, $PAGE;

        //Check for reference xml in string
        if (empty($text)) {
            return $text;
        }

        if (strpos($text, '&lt;references&gt;') === false &&
                strpos($text, '&amp;lt;references&amp;gt;') === false &&
                strpos($text, '<references>') === false) {
            //no references tag detected so return
            return $text;
        }

        //Only require libs when we are sure we need them.
        require_once($CFG->libdir . '/ajax/ajaxlib.php');

        include_once(dirname(__FILE__).'/export_references.php');

        self::$counter++;

        //create a semi-random num to identify this filter session (for ids in html)
        self::$randnum = rand(1, 20);

        //Work out if <references> tag(s) are in $text, account for html entities (<=&lt;)
        //get just the xml (can't use regex as this keep falling over on big strings!)
        if (strpos($text, '&amp;lt;references&amp;gt;') !== false) {
            $text = htmlspecialchars_decode($text);
        }

        if (strpos($text, '&lt;references&gt;') !== false) {
            $textarr = explode('&lt;references&gt;', $text);
            //go thru array and if el has references closing tag then get string from start to this
            for ($a=0, $max=count($textarr); $a<$max; $a++) {
                $textstr = $textarr[$a];
                $endtagpos = strpos($textstr, '&lt;/references&gt;');
                if ($endtagpos !== false) {
                    $textstr = substr($textstr, 0, $endtagpos);
                    $textstr = strip_tags($textstr);//We have to strip tags here as moodle tries to make URLs into <a> tags etc when using format_text
                    //convert < and > from htm ents
                    $textstr = str_replace('&lt;', '<', $textstr);
                    $textstr = str_replace('&gt;', '>', $textstr);
                    $textstr = str_replace('&quot;', '"', $textstr);
                    //add references tag
                    $textstr = '<references>' . $textstr . '</references>';
                    //send to function to get replacement text
                    $reptext = self::references_filter_replace($textstr, $a);
                    //replace the text in el with replacement
                    $textarr[$a] = $reptext.substr($textarr[$a], $endtagpos+19);
                }
            }
            //replace text with updated ver
            $text = implode('', $textarr);
        }
        if (strpos($text, '<references>') !== false) {
            $textarr = explode('<references>', $text);
            //go thru array and if el has references closing tag then get string from start to this
            for ($a=0, $max=count($textarr); $a<$max; $a++) {
                $textstr = $textarr[$a];
                $endtagpos = strpos($textstr, '</references>');
                if ($endtagpos !== false) {
                    $textstr = substr($textstr, 0, $endtagpos);
                    //add references tag
                    $textstr = '<references>' . $textstr . '</references>';
                    //send to function to get replacement text
                    $reptext = references_filter_replace($textstr, $a);
                    //replace the text in el with replacement
                    $textarr[$a] = $reptext . substr($textarr[$a], $endtagpos+13);
                }
            }
            $text = implode('', $textarr);
        }
        $link = new moodle_url('/filter/references/export_references.php');
        $PAGE->requires->js_init_call('M.filter_references.init_ref_form', array((string)$link));
        return $text;
    }
    /**
     * Returns replacement text for the text string sent
     * @param $textstr string: references xml string
     * @return string
     */
    private static function references_filter_replace($textstr, $counter) {
        global $CFG, $COURSE;
        //remove br and returns added by html editor (use str_replace as quicker than regex)
        $textstr = str_ireplace('<br />', '', $textstr);
        $textstr = str_ireplace('<br/>', '', $textstr);
        $textstr = str_replace("\n", '', $textstr);

        //convert to xml
        $doc = new DOMDocument('1.0', 'utf-8');
        $doc->preserveWhiteSpace = false;
        $doc->formatOutput = false;
        //custom handler to trap any messed-up xml (so no fatal error)
        try {
            set_error_handler(array('filter_references', 'references_HandleXmlError'));
            $doc->loadXML($textstr);
            restore_error_handler();
        } catch(DOMException $error) {
            return '';//return nothing if error
        }

        $referencetags = $doc->getElementsByTagName('reference');

        //If there is a linking library send data to that to get array of links
        //if (class_exists('linking')) {
        if (file_exists(dirname(__FILE__).'/../../local/references/linking.php')) {
            @include_once(dirname(__FILE__).'/../../local/references/linking.php');

            $linksarray = linking::create_link($doc);
            //add links to xml <reference link=""
            for ($a=0, $max=$referencetags->length; $a<$max; $a++) {
                if (isset($linksarray[$a]) && $linksarray[$a]!=null) {
                    $linkatt = $doc->createAttribute('link');
                    $linkatt->nodeValue = htmlentities($linksarray[$a]);
                    $referencetags->item($a)->appendChild($linkatt);
                }
            }
        }

        if (file_exists(dirname(__FILE__).'/../../local/references/export.php')) {
            //add in info that will be used to create an export menu for each ref (only creates this if export lib file available)
            //get info from references_filter_export
            $exportlist = references_filter_export::get_formats();
            //create nodes in $doc <menu><export><opt name title desc ajax/></export><download><opt...
            $menunode=$doc->createElement('menu');
            $exportnode=$doc->createElement('export');
            $downnode=$doc->createElement('download');
            for ($a=0, $max=count($exportlist); $a<$max; $a++) {
                $curformat=&$exportlist[$a];
                //create opt node with atts
                $optnode = $doc->createElement('opt');
                $att = $doc->createAttribute('name');
                $att->nodeValue = $curformat['name'];
                $optnode->appendChild($att);
                //title (display name)
                $att = $doc->createAttribute('title');
                $att->nodeValue = strip_tags($curformat['disname']);
                $optnode->appendChild($att);
                //description
                $att = $doc->createAttribute('desc');
                $att->nodeValue = strip_tags($curformat['description']);
                $optnode->appendChild($att);
                //ajax off on this export?
                $ajaxval = 1;
                if (isset($curformat['noajax']) || $curformat['type'] == references_filter_export::DOWNLOAD) {
                    $ajaxval = 0;
                }
                $att = $doc->createAttribute('ajax');
                $att->nodeValue = $ajaxval;
                $optnode->appendChild($att);

                //add to correct node
                if ($curformat['type'] == references_filter_export::DOWNLOAD) {
                    $downnode->appendChild($optnode);
                } else if ($curformat['type'] == references_filter_export::EXPORT) {
                    $exportnode->appendChild($optnode);
                }

            }
            $menunode->appendChild($exportnode);
            $menunode->appendChild($downnode);
            $doc->appendChild($menunode);
        }

        //Transform our refxml data into (x)html using xslt
        // Load XSL template
        $xsl = new DOMDocument();
        if (!$xsl->load(dirname(__FILE__).'/xml2html.xsl')) {
            return '';
        }

        // Create new XSLTProcessor
        $xslt = new XSLTProcessor();
        // Load stylesheet
        if (!$xslt->importStylesheet($xsl)) {
            return '';
        }

        //Number param so all html id on page are unique
        //$xslt->setParameter('','num',self::$counter);//number of times called filter
        //Number is set to a random number to ensure is always unique (only one )
        $xslt->setParameter('', 'num', self::$randnum);
        $xslt->setParameter('', 'count', $counter);//number of references tag in this call
        $xslt->setParameter('', 'sesskey', sesskey());
        //page references will be submitted to on export
        $xslt->setParameter('', 'postto', $CFG->wwwroot.'/filter/references/export_references.php');
        $xslt->setParameter('', 'downloadhead', get_string('refexport_downloadto', 'local_references'));
        $xslt->setParameter('', 'exporthead', get_string('refexport_exportto', 'local_references'));
        $xslt->setParameter('', 'exportbutname', get_string('exportbutname', 'filter_references'));
        $xslt->setParameter('', 'seltitle', get_string('seltitle', 'filter_references'));
        //image for submit button
        $xslt->setParameter('', 'imageloc', $CFG->wwwroot.'/local/references/images/export_16.png');
        //Send info to the export about what course we came from
        $xslt->setParameter('', 'courseid', $COURSE->id);
        $xslt->setParameter('', 'coursename', $COURSE->shortname);

        $xslt->setParameter('', 'ajaxenabled', 1);//In Moodle 2 version just set to true as no YUI anymore

        $xslt->setParameter('', 'noajax', get_string('noajax', 'filter_references', $CFG->wwwroot.'/filter/references/export_references.php?button=null&amp;info='.sesskey().'~~'.$COURSE->shortname.'~~'.$COURSE->id));
        $xslt->setParameter('', 'noajax2', get_string('noajax2', 'filter_references'));
        $xslt->registerPHPFunctions();

        $results = $xslt->transformToDoc($doc);

        //remove doctype
        $result = preg_replace('/<!DOCTYPE[^>]*>/', ' ', $results->saveXML());
        $result = str_replace('<?xml version="1.0" encoding="utf-8"?>', '', $result);
        $result = preg_replace('/\s\s/', '', $result);
        $result = str_replace('xmlns:php="http://php.net/xsl"', '', $result);
        //remove other junk from doc
        //$result = str_replace('&nbsp;&nbsp;&nbsp;','',$result);
        $result = str_replace("\t", '', $result);
        return $result;
    }


    public static function references_HandleXmlError($errno, $errstr, $errfile, $errline) {
        if ($errno==E_WARNING && (substr_count($errstr, "DOMDocument::loadXML()") > 0)) {
            throw new moodle_exception('reference_xmlloaderror', 'references');
        } else {
            return false;
        }
    }
}
?>